package tjs.study.notes.dotest.jvm.class_6_1_类文件结构;

import tjs.study.notes.base.common.TestBuiUtils;

/**
 * *运行时常量池（即方法区其他部分）：
 * jdk1.6/jdk1.7: 方法区（永久代）中  （包含在jvm的内存中）
 * jdk1.8       : 元空间中          （独立于jvm的内存外）
 * <p>
 * *字符串常量池/静态变量：
 * jdk1.6: 方法区（永久代）中
 * jdk1.7: 堆中
 * jdk1.8: 堆中
 * <p>
 * *常量池的意义
 * 常量池是为了避免频繁的创建和销毁对象而影响系统性能，其实现了对象的共享
 * 节省内存空间，运行时间
 *
 * @since 02.12.10
 */
public class 常量池 {


    public static void main(String[] args) {
        testString();
//        testStringIntern();
//        testOtherPool();
        testStringAndStringBuilder();

        testDifferClass();
    }


    /**
     * String b1 = "hellow"; String a1 = b1 + "world";
     * fixme 编译后可以发现字符串直接使用+号拼接底层是使用的StringBuilder实现的，其等价于a1 = new StringBuilder().apend(b1).apend(world).toString
     * 而每一次StringBuilder.toString，每次都会new一个新的String，所以就导致了以下两个问题
     * 1.new一个新的对象需要开辟新的空间，花费时间
     * 2.在堆中旧的String，没有被任何栈引用，浪费了空间
     * <p>
     * 而StringBuilder可能会动态扩容，所以实际上就是在比较new String和Arrays.copyOf的效率对比
     */
    public static void testStringAndStringBuilder() {
        final int testTimes = 9999;
        new TestBuiUtils("测试String拼接效率", (taskNum) -> {//max:479; min:35; avg:119; sum:2382
            String res = "123";
            for (int i = 0; i < testTimes; i++) {
                res = res + i;//res = new StringBuilder().apend(res).apend(i).toString
            }
            res.toString();
        }).doTest().printAll();

        new TestBuiUtils("测试StringBuilder拼接效率", (taskNum) -> {//max:1; min:0; avg:0; sum:4
            StringBuilder res = new StringBuilder("123");
            for (int i = 0; i < testTimes; i++) {
                res = res.append(i);
            }
            res.toString();
        }).doTest().printAll();

        new TestBuiUtils("测试StringBuffer拼接效率", (taskNum) -> {//max:1; min:0; avg:0; sum:4
            StringBuffer res = new StringBuffer("123");
            for (int i = 0; i < testTimes; i++) {
                res = res.append(i);
            }
            res.toString();
        }).doTest().printAll();
    }

    public static void testString() {
        // target位于堆中的常量池
        final String target = "hellowworld";

        String target2 = "hellowworld";
        String target2_1 = new String("hellowworld");
        System.out.println("target2:" + (target == target2));//true
        System.out.println("target2_1:" + (target == target2_1));//false


        // a1位于堆中
        String b1 = "hellow";
        String a1 = b1 + "world";//运行是才能确定b1的值
        System.out.println("a1:" + (target == a1));//false

        // a2位于堆中的常量池
        final String b2 = "hellow";
        String a2 = b2 + "world";
        System.out.println("a2:" + (target == a2));//true


        // a3位于堆中的常量池
        String a3 = "hellow" + "world";
        System.out.println("a3:" + (target == a3));//true

        // a4位于堆中
        String a4 = new String("hellow") + "world";
        System.out.println("a4:" + (target == a4));//false

        // a5.toString() toString的实现是在堆中重新new了一个String
        StringBuilder a5 = new StringBuilder("hellowworld");
        System.out.println("a5:" + (target == a5.toString()));//false
    }

    /**
     * String的String Pool（字符串常量池）
     * 是一个固定大小的Hashtable，默认值大小长度是1009。如果放进String Pool的String非常多，就会造成Hash冲突严重，从而导致链表会很长，
     * 而链表长了后直接会造成的影响就是当调用String.intern()方法时性能会大幅下降
     * <p>
     * strxxx.intern()方法
     * jdk1.6：     如果运行时常量池中存在strxxx则返回引用，不存在(首次)则将新生成一个值等于strxxx的对象缓存到运行时常量池中，返回常量池引用
     * jdk1.7及以后：如果运行时常量池中存在strxxx则返回引用，不存在(首次)则将当前strxxx的堆引用（仅可能）缓存到运行时常量池中，返回堆引用/常量池引用
     * <p>
     * fixme 如果.intern指向堆中那个引用被回收了，则同一个字符串再次调用intern就会指向不同的堆引用
     * 虽然String.intern()的返回值永远等于字符串常量。但这并不代表在系统的每时每刻，相同的字符串的intern()返回都会是一样的（虽然在95%以上的情况下，都是相同的）。
     * 因为存在这么一种可能：在一次intern()调用之后，该字符串在某一个时刻被回收，之后，再进行一次intern()调用，那么字面量相同的字符串重新被加入常量池，但是引用位置已经不同
     */
    public static void testStringIntern() {
        final String target1 = "hellowworld"; // target1位于堆中的常量池
        String a1 = new String("hellow") + "world";
        System.out.println("Intern a1:" + (a1.intern() == target1));//true a1.intern()->常量池中target1的引用
        System.out.println("Intern a11:" + (a1.intern() == a1));//false   a1->堆中String a1的引用

        String a2 = new String("hellow") + "world2";
        System.out.println("Intern a2:" + (a2.intern() == "hellowworld2"));//true a2.intern()->堆中String a2的引用  常量池中"hellowworld2"的引用->堆中String a2的引用
        System.out.println("Intern a22:" + (a2.intern() == a2));//true   a2.intern()->堆中String a2的引用
        final String target2 = "hellowworld2"; // target2位于堆中的常量池的引用->堆中String a2的引用
        System.out.println("Intern a22:" + (a2 == target2));//true
    }

    public static void testOtherPool() {
        /**
         * 解析.valueOf方法可以发现，Integer/Long的常量池是一个静态数组
         * 范围：-128~127
         * @see Integer#valueOf(int)
         *      @see Integer.IntegerCache#cache
         * @see Long#valueOf(long)
         *      @see Long.LongCache#cache
         *
         */
        Integer i1_1 = 127;
        Integer i1_2 = 127;
        System.out.println("Integer i1:" + (i1_1 == i1_2));//true


        Integer i2_1 = 128;
        Integer i2_2 = 128;
        System.out.println("Integer i2:" + (i2_1 == i2_2));//false


        int i3_1 = 130;
        int i3_2 = 130;
        System.out.println("int i2:" + (i3_1 == i3_2));//true

        //double 没有实现池化
        Double d1 = 1.0;
        Double d2 = 1.0;
        System.out.println(d1 == d2); //false

    }

    static class A {
        public static final Integer a123 = 130;
        public static final Integer a123_2 = 130;
        public static Integer b123 = 132;
        public static Integer b123_2 = 132;
        public static final int c123 = 131;
        public static final double d123 = 1d;
    }

    static class B {
        public static final Integer a123 = 130;
        public static Integer b123 = 132;
        public static final int c123 = 131;
        public static final double d123 = 1d;

    }

    public static void testDifferClass() {
        A a = new A();
        B b = new B();
        System.out.println("a.a123 == a.a123_2:" + (a.a123 == a.a123_2));//false
        System.out.println("a.b123 == a.b123_2:" + (a.b123 == a.b123_2));//false
        System.out.println("a.a123 == b.a123:" + (a.a123 == b.a123));//false
        System.out.println("a.b123 == b.b123:" + (a.b123 == b.b123));//false
        System.out.println("a.c123 == b.c123:" + (a.c123 == b.c123));//true
        System.out.println("a.d123 == b.d123:" + (a.d123 == b.d123));//true
    }
}
